package com.bootlever.framework.rmi.configuration;

import com.bootlever.framework.common.utils.PropertyUtils;
import com.bootlever.framework.common.utils.StringUtils;
import com.bootlever.framework.rmi.annotation.RmiServer;
import com.bootlever.framework.rmi.proxy.RmiProxyFactory;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.io.IOException;
import java.util.Map;
import java.util.Set;

public class RmiRegistrarBean implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;


    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        Map<String, Class<?>> map = scan();
        if (map != null && !map.isEmpty()){
            for (String beanName : map.keySet()){
                Class<?> clazz = map.get(beanName);
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
                GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
                definition.getPropertyValues().add("interfaceClass", definition.getBeanClassName())
                .add("applicationContext", applicationContext);
                definition.setBeanClass(RmiProxyFactory.class);
                definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
                registry.registerBeanDefinition(beanName, definition);
            }
        }
    }

    private Map<String,Class<?>> scan() {
        Map<String, Class<?>> map = Maps.newConcurrentMap();
        Set<Class> set = Sets.newConcurrentHashSet();
        try {
            String basePackage = PropertyUtils.getProperty("/application.properties","bootlever.app.basePackage");
            Reflections reflections = null;
            if (StringUtils.isNotBlank(basePackage)){
                reflections = new Reflections(basePackage, new TypeAnnotationsScanner(), new SubTypesScanner());
                set.addAll(reflections.getTypesAnnotatedWith(RmiServer.class));
            }
            String rmiClientPackages = PropertyUtils.getProperty("/application.properties","bootlever.app.rmiClientPackage");
            if (StringUtils.isNotBlank(rmiClientPackages)) {
                String[] packages = rmiClientPackages.split(",");
                for (String p : packages){
                    reflections = new Reflections(p, new TypeAnnotationsScanner(),new SubTypesScanner());
                    set.addAll(reflections.getTypesAnnotatedWith(RmiServer.class));
                }
            }
            for (Class<?> clazz : set){
                if (clazz.getAnnotation(RmiServer.class) == null){
                    continue;
                }
                RmiServer rmiServer = clazz.getAnnotation(RmiServer.class);
                String name = rmiServer.name();
                if (StringUtils.isEmpty(name)){
                    name = clazz.getSimpleName();
                    String first = name.substring(0,1);
                    name = first.toLowerCase() + name.substring(1, name.length());
                }
                map.putIfAbsent(name, clazz);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            return map;
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
